#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/*
指针：就是地址
指针与变量：
    int a=10;
    int *p=&a;
    不能写作  *p=a;  p=1122;
野指针与空指针
    int *p;//野指针
    int *p=NULL;//空指针   为了防止野指针
指针与一维数组
    int num[10];
    int *p=num;//num就是首地址  
指针数组与数组指针
    指针数组  本质是一个数组  与数据数组没什么很大的区别 唯一的区别在于数值数组存的是数值 而指针数组 每一个元素存的是指针
    数组指针 本质是一个指针  是一个行指针   可以直接指向一个一维数组
函数指针
    返回值  （*函数指针变量名）（参数类型列表）；
函数指针数组
    返回值 （*函数指针数组名[数组长度]） (参数类型列表)；
多级指针
   int **p2;int ***p3;
指针与字符串
    char *q="hello";   这里是值  指针变量q指向了  字符串常量 "hello"的首地址  并不是将hello赋值给指针变量p
回调函数
        函数作为参数传递给另一个函数，回调函数主要用于主调函数不方便调用实现函数时使用
主函数的参数
        int argc, char const *argv[]
        int argc:终端传入命令的条数
        char const *argv[]：终端传入的命令从参数   常量指针数组：数据不能被改变
万能指针
   void *p;

内存管理
    int *arr=(int *)malloc(sizeof(int )*5); //不会初始化
    arr=(int*)calloc(5,sizeof(int));//会初始化
    arr=relloc(arr,sizeof(int)*10);//再原来的基础上加到 10个长度 
    free：释放  一定要记得释放
自定义数据类型
文件操作


*/
void pointer_1(void);//指针与一维数组
void pointer_2(void);//数组指针 与 指针数组 访问二维数组
void pointer_3(void);//函数指针
void pointer_4(void);//函数指针数组
void pointer_5(void);//多级指针
void pointer_6(void);//指针与字符串常量
void pointer_7(void);//字符指针数组
void pointer_8(void);//万能指针

void memory(void);//内存管理
//回调函数
void func(void);//校长  
int main(int argc, char const *argv[])
{
    /* code */
    // for(int i=0;i<argc;i++)
    // {
    //     printf("%s\n",argv[i]);//argc[i]是一个指针/字符串  
    // }
    // int a=atoi(argv[1]);//atoi是将数字字符串转换为整型数据的函数  itoa  将整型数据转换为字符串
    // // int a;
    // // sscanf(argv[1],"abc%d",&a);//如果你有带格式的数据 那就按照指定的格式分解数据出来  进行格式化转换
    // printf("a=%d\n",a);
    // int b=atoi(argv[3]);
    // int res=0;
    // switch(argv[2][0])//switch 里面的表达式 整型/字符型/枚举类型变量
    // {
    //     case '+':
    //         res=a+b;
    //     break;
    //     case '-':
    //         res=a-b;
    //     break;
    //     case '*':
    //         res=a*b;
    //     break;
    //     case '/':
    //         res=a/b;
    //     break;
    // }
    // printf("res=%d\n",res);
    // pointer_3();
    //  pointer_4();
    //  pointer_5();
    //pointer_6();
   // pointer_7();
 //  func();
 //pointer_8();
 //内存管理
 memory();
    return 0;
}
//内存管理
void memory(void)
{
   // malloc函数向堆区申请一段内存空间
   //向堆区申请  5个int类型的字节空间  一共是20个字节
   int *arr=NULL;//空指针
   arr=(int *)malloc(sizeof(int)*5);
   //现在相当于arr指向了刚才分配的20个字节的空间的首地址  所以 可以将arr当作数组名使用
   printf("请输入5个整数：");
   for(int i=0;i<5;i++)
   {
    scanf("%d",arr+i);
   }
      for(int i=0;i<5;i++)
   {
    printf("%d ",arr[i]);
   }
   printf("sizeof(arr):%lu\n",sizeof(arr));//8个字节  
   //realloc  给刚才分配的空间扩容  但是 这个字节大小并不是扩容空间 而是总空间字节大小
   arr=realloc(arr,sizeof(int)*7);
   arr[5]=arr[6]=100;
    for(int i=0;i<7;i++)
   {
    printf("%d ",arr[i]);
   }
   //一定要记得释放
   free(arr);

   //calloc
   arr=calloc(5,sizeof(int));
    printf("请输入5个整数：");
   for(int i=0;i<5;i++)
   {
    scanf("%d",arr+i);
   }
      for(int i=0;i<5;i++)
   {
    printf("%d ",arr[i]);
   }
   free(arr);
}
void print(int i)
{
printf("%d\t",i);
}
//
void disp(int n,void (*p)(int) )
{
    p(n);
}
//回调函数
void func(void)//校长  
{
    //disp ：班主任 print:xx同学
    disp(10,print);
}

//万能指针
void pointer_8(void)
{
    void *p=NULL;//void 类型指针   万能指针  
    int a=10;//整型变量
    char ch='a';
    p=(void *)(&a);//当前a是整型 
    //如果想通过 p访问变量a
    *(int *)(p)=100;
    printf("a=%d\n",a);
    p=(void *)&ch;
    printf("*p:%c\n",*((char *)p));
}
void pointer_7(void)
{
    char *p[]={"1.选座","2.购票","3.显示详情"};//指针数组  每一个元素分别指向了一个字符串常量
    //p[i]就是一个指针   分别指向一个字符串常量
    int i;
    for(i=0;i<3;i++)
    {
        puts(p[i]);
    }
}
void pointer_6()
{
    printf("指针与字符串:\n");
    char str[6]={"hello"};
    char *p=str;//定义了一个字符类型指针变量 p指向字符数组str的首地址
    //可以通过p访问字符串常量"hello“
    puts(p);
    *p='a';//在这里可以修改数据  编译时没有报错  
    puts(p);
    //p修改指向的 
    p="hello";//指向  字符串常量"hello"所在的首地址  "hello"可以当作首地址来使用
  puts(p);
  printf("\"hello\"[4]:%c,p[4]:%c\n","hello"[4],p[4]);
  *p='a';//编译时没有报错  输出时数据不对
    puts(p);

   // 注意  字符数组是不能直接赋值的  字符串之所有能赋值给字符指针，实际是指针指向了该字符串常量的首地址，而并不是将字符串赋值给了指针
   char buf[20]={"world"};
  // buf="hello";//xxxx   不可这么写  会报错

}
void pointer_5(void)
{
    printf("多级指针\n");
    int a=10;
    int *p=&a;//p指向a的地址
    int **p2=&p;//p2指向p的地址
    int ***p3=&p2;
    int ****p4=&p3;
    printf("a=%d,*p=%d,**p2=%d,***p3=%d,****p4=%d",a,*p,**p2,***p3,****p4);
}
void pointer_4(void)
{
    printf("函数指针数组：\n");
    void (*p_fun[2])();//函数指针数组    int *p[2];//指针数组   看作是一个数组  
    p_fun[0]=pointer_1;//数组的第一个元素  指向了pointer_1函数的地址
    p_fun[0]();//调用方法1/在数组中 每一个元素可以当作是一个变量去使用
    p_fun[1]=pointer_2;
     (*(p_fun+1))();//(*p_fun[1])();//在数组中 p_fun+1  就是&p_fun[1]
}
void pointer_3(void)
{
    printf("函数指针：\n");
    void (*p_fun)();//函数指针  可以指向一个没有返回值 没有参数的函数  
    p_fun=pointer_1;//函数指针p_fun指向了pointer_p函数的地址   pointer_1函数的首地址
    p_fun();//调用方法1  调用的pointer_1函数
    p_fun=pointer_2;///函数指针p_fun指向了pointer_2   int a=10;int *p=&a;  *p访问a
    (*p_fun)();//调用方法2  *p_fun就是pointer_2
}
//
void pointer_2(void)
{
    printf("指针数组与数组指针\n");
    int num[4][5]={0};
    int *p_num[4];//可以指向4个int类型的地址
    int (*q_num)[5];//可以指向一个含5个元素的一维数组
    //通过指针数组来访问二维数组
    for(int i=0;i<4;i++)
    p_num[i]=num[i];//p_num[i]指向了  &num[i][0];
//通过指针数组来对二维数组的每一个元素进行赋值
    for(int i=0;i<4;i++)
    {
        for(int j=0;j<5;j++)
        {
            *(*(p_num+i)+j)=101+i*5+j;
        }
    }
    //通过下标法遍历打印数组元素值
    for(int i=0;i<4;i++)
    {
        for(int j=0;j<5;j++)
        {
           printf("%d\t",num[i][j]);
        }
        printf("\n");
    }
    printf("------\n");
    //通过数组指针 来访问输出  数组元素的值
    q_num=num;
    for(int i=0;i<4;i++)//q_num指向下一行
    {
        for(int j=0;j<5;j++)
        {
           printf("%d\t",*(*(q_num+i)+j));
        }
         printf("\n");
    }

}
void pointer_1(void)
{
    printf("指针与一维数组\n");
    int num[10]={11,22,33,44,55,66,77,88,99,110};
    int *p=num;//p指向了 num[0]
    //在下面循环中  p循环的指向数组num的每一个元素
    // for(;p<num+10;p++)//num+i  表示  &num[i]
    // {
    //     printf("*p=%d ",*p);
    // }
    // printf("\n");
    for(int i=0;i<10;i++)
    {
        printf("%d\t",*(p+i));
    }
    //重新让p指向num[0]  
    p=num;
    printf("%d,%d,%d\n",*p++,(*p)++,++(*p));//printf函数每个编译器执行循环会有所不同  在这里先执行 ++(*p)，再执行 (*p)++,最后执行 *p++，然后再从左往右输出 
    printf("%d\n",*p);
}
